import json
import locale
import time
import tkinter as tk
from tkinter import ttk

from selenium import webdriver
from selenium.webdriver.common.by import By

from selenium.webdriver.support.ui import WebDriverWait  # 显示等待
from selenium.webdriver.support import expected_conditions as ec  # 等待的条件
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
from tkcalendar import DateEntry

# 创建浏览器对象
driver = webdriver.Chrome()
driver.maximize_window()

# 设置区域环境为中文
try:
    locale.setlocale(locale.LC_TIME, 'zh_CN.UTF-8')  # 对于中文（简体）
except locale.Error:
    try:
        locale.setlocale(locale.LC_TIME, 'chinese')  # 旧的locale设置
    except locale.Error:
        print("你的系统可能不支持中文locale设置。")


def check_no_filter_ticket_display():
    no_filter_ticket_6_div = driver.find_element(by=By.ID, value="no_filter_ticket_6")
    no_filter_ticket_2_div = driver.find_element(by=By.ID, value="no_filter_ticket_2")

    # 如果页面出现超时情况，则继续查询余票
    div_6_display_value = no_filter_ticket_6_div.get_attribute("display")
    div_2_display_value = no_filter_ticket_2_div.get_attribute("display")
    if div_2_display_value or div_6_display_value:
        return True
    else:
        return False


# 点击查询余票的查询按钮，查询余票
def click_query_ticket():
    WebDriverWait(driver, 1000).until(
        ec.element_to_be_clickable((By.ID, "query_ticket"))
    )
    query_ticket_tag = driver.find_element(by=By.ID, value="query_ticket")
    query_ticket_tag.click()


def now_date():
    format_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
    return format_time


# 校验查询车次的席位，判断是否有位，有位返回true
def has_seat(count):
    if count.isdigit() or count == "有":  # 判断是否有票
        return True
    else:
        return False


def check_has_seat(infos, seat_types):
    """
    高铁：
    索引8-商务座特等座
    索引9-一等坐
    索引10-二等座二等包座
    索引11-高级软卧

    普通列车：
    索引12-软卧/动卧一等卧
    索引13-硬卧二等卧
    索引14-软坐
    索引15-硬座
    索引16-无座
    索引17-其他
    """
    is_flag = False  # 标记是否有余票，没有余票为false,有余票为true
    seat_type_name = None  # 席位名称

    print(f"seat_types:{seat_types}")
    print(f"infos[10]:{infos[10]}")
    print(f"infos[9]:{infos[9]}")
    print(f"infos[15]:{infos[15]}")
    print(f"infos[16]:{infos[16]}")
    print(f"infos[13]:{infos[13]}")
    print(f"infos[12]:{infos[12]}")

    for seat_type in seat_types:  # 遍历要查询的席位列表
        # 说明是二等座,索引为10的是二等坐
        if seat_type == "O" and has_seat(infos[10]):
            is_flag = True
            seat_type_name = "二等坐"
            break
        # 说明是一等座,索引为9的是一等坐
        elif seat_type == "M" and has_seat(infos[9]):
            is_flag = True
            seat_type_name = "一等坐"
            break
        # 1-硬座,索引15-硬座
        elif seat_type == "1" and has_seat(infos[15]):
            is_flag = True
            seat_type_name = "硬坐"
            break
        # 1-硬座,索引16-无座
        elif seat_type == "1" and has_seat(infos[16]):
            is_flag = True
            seat_type_name = "无坐"
            break
        # 3-硬卧,索引13-硬卧二等卧
        elif seat_type == "3" and has_seat(infos[13]):
            is_flag = True
            seat_type_name = "硬卧"
            break
        # 4-软卧,12-软卧/动卧一等卧
        elif seat_type == "4" and has_seat(infos[12]):
            is_flag = True
            seat_type_name = "软卧"
            break
        if not is_flag:
            continue
    return is_flag, seat_type_name


class TrainSpider_v1():
    login_url = "https://kyfw.12306.cn/otn/resources/login.html"  # 登录页面
    profile_url = "https://kyfw.12306.cn/otn/view/index.html"  # 个人中心页面
    left_ticket = "https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc"  # 余票查询接口
    confirm_url = "https://kyfw.12306.cn/otn/confirmPassenger/initDc"  # 确认乘车人和坐席

    # 定义init初始化方法
    def __init__(self, from_station, to_station, train_date, query_trains, passenger_name_lst):
        self.from_station = from_station
        self.to_station = to_station
        self.train_date = train_date
        self.station_code = self.init_statsion_code()
        self.query_trains = query_trains
        self.passenger_name_lst = passenger_name_lst
        self.selected_no = None  # 选中的车次
        self.selected_seat = None  # 选中的坐席

    # 登录
    def login(self):
        # 打开登录的页面
        driver.get(self.login_url)
        # 打开后要等待一会，等待扫码登录
        WebDriverWait(driver, 1000).until(
            ec.url_to_be(self.profile_url)  # 直到url页面变成个人中心页面
        )
        print("登录成功,开始为你抢票")

    # 查询余票
    def search_ticket(self):
        # 打开余票的地址
        driver.get(self.left_ticket)

        # 设置查询条件
        self.set_query_param()

        # 循环查询余票
        while True:
            # 找到查询按钮，并进行单击操作
            click_query_ticket()

            # 如果页面出现超时情况，则继续查询余票
            no_ticket = check_no_filter_ticket_display()
            if no_ticket:
                continue

            # 获得有票的车次信息
            is_flag, seat_type_name, has_ticket_train, train_no = self.get_train_info()

            # 如果有票点击预定，并退出最外层循环
            if is_flag:
                print(now_date(), f"当前车次【{train_no}-{seat_type_name}】有余票,开始为你预订!!")
                self.selected_no = train_no  # 记录选中的车次
                order_btn = has_ticket_train.find_element(by=By.XPATH, value=".//a[@class=\"btn72\"]")
                # No 'Access-Control-Allow-Origin' header is present on the requested resource
                order_btn.click()
                break  # 如果有票，退出最外层循环
            else:
                time.sleep(0.2)

    def get_train_info(self):
        is_flag = False  # 标记是否有余票，没有余票为false,有余票为true
        train_no = None  # 当前有票的车次
        seat_type_name = None  # 席位名称
        has_ticket_train = None  # 有票的车次信息,返回的是一行记录

        # 筛选出有数据的tr,去掉属性为datatran的tr
        trains = driver.find_elements(by=By.XPATH, value="//tbody[@id=\"queryLeftTable\"]/tr[not(@datatran)]")
        # 分别遍历每个车次
        for train in trains:
            # 将回车换行替换成空格，并按空格切分成数组
            infos = train.text.replace("\n", " ").split(" ")
            # 是否有票标识,当前有票的车次,席位名称
            is_flag, train_no, seat_type_name = self.check_has_ticket(infos)
            if is_flag:
                order_btn_list = train.find_elements(by=By.XPATH, value=".//a[@class=\"btn72\"]")
                # 增加有票，且预订按钮可点击的判断
                if len(order_btn_list) > 0:
                    has_ticket_train = train
                    break
                else:
                    is_flag = False
                    print(now_date(), f"当前车次【{train_no}】不可预定,继续为你抢票!!")

        return is_flag, seat_type_name, has_ticket_train, train_no

    # 校验是否有票
    def check_has_ticket(self, infos):
        train_no = infos[0]  # 列表中索引为0的为车次
        print(f"train_no:{train_no}")
        is_flag = False
        seat_type_name = None
        for query_train in self.query_trains:  # 要购买的车次列表
            if train_no not in query_train:
                continue

            # 校验是否有席位
            is_flag, seat_type_name = check_has_seat(infos, query_train[train_no])

            # 如果有席位,跳出循环,返回有席位的车次信息
            if is_flag:
                break
            else:
                print(now_date(), f"当前车次【{train_no}】没有余票,继续为你抢票!!")

        return is_flag, train_no, seat_type_name

    # 设置查询条件
    def set_query_param(self):
        # 找到出发站和到达站的隐藏的HTML标签
        from_station_input = driver.find_element(by=By.ID, value="fromStation")
        from_station_text = driver.find_element(by=By.ID, value="fromStationText")

        to_station_input = driver.find_element(by=By.ID, value="toStation")
        to_station_text = driver.find_element(by=By.ID, value="toStationText")

        # 找到出发时间的input标签
        train_date_input = driver.find_element(by=By.ID, value="train_date")

        # 根据车站名称获取车站编码
        from_station_code = self.station_code[self.from_station]  # 根据出发地获取出发地的代号
        to_station_code = self.station_code[self.to_station]  # 根据上目的地获取目的地的代号
        print(from_station_code, to_station_code)
        # 执行js,将车站编码及出发日期写到input标签中
        driver.execute_script("arguments[0].value=\"%s\"" % from_station_code, from_station_input)
        driver.execute_script("arguments[0].value=\"%s\"" % self.from_station, from_station_text)

        driver.execute_script("arguments[0].value=\"%s\"" % to_station_code, to_station_input)
        driver.execute_script("arguments[0].value=\"%s\"" % self.to_station, to_station_text)

        driver.execute_script("arguments[0].value=\"%s\"" % self.train_date, train_date_input)

    def confirm(self):
        # 等待来到确认乘车人页面
        WebDriverWait(driver, 1000).until(
            ec.url_to_be(self.confirm_url)
        )
        # 等待乘车人列表出来
        WebDriverWait(driver, 1000).until(
            ec.presence_of_element_located((By.XPATH, "//ul[@id=\"normal_passenger_id\"]/li/label"))
        )
        print(now_date(), "开始获取乘车人信息")
        # 获取所有乘车人
        passagers = driver.find_elements(by=By.XPATH, value="//ul[@id=\"normal_passenger_id\"]/li/label")
        for passager in passagers:  # 遍历每个乘车人-label标签
            name = passager.text  # 获取乘车人的姓名
            if name in self.passenger_name_lst:  # 选中乘车人
                passager.click()  # label标签的单击操作

        # 确认席位
        seat_select = Select(driver.find_element(by=By.ID, value="seatType_1"))
        seat_types = None
        for train in self.query_trains:
            if self.selected_no in train:
                seat_types = train[self.selected_no]  # 获得想要抢票车次的所有席位，self.selected_no是选中车次的键
                break

        selected_seat_flag = False
        for seat_type in seat_types:
            try:
                seat_select.select_by_value(seat_type)  # 选中席位
                self.selected_seat = seat_type  # 记录有票的坐席
            except NoSuchElementException:
                continue
            else:
                selected_seat_flag = False
                break  # 如果有席位，则退出

        # 没有想要的席位,退出重新抢票
        if not selected_seat_flag:
            print(now_date(), f"当前车次【{self.selected_no}】没有想要的席位,继续为你抢票!!!")
            return False

        # 等待提交订单按钮可点击
        WebDriverWait(driver, 1000).until(
            ec.element_to_be_clickable((By.ID, "submitOrder_id"))
        )
        # 提交订单
        submit_btn = driver.find_element(by=By.ID, value="submitOrder_id")
        submit_btn.click()

        # 等待出现模式对话框出现，核对购票信息
        WebDriverWait(driver, 1000).until(
            ec.presence_of_element_located((By.CLASS_NAME, "dhtmlx_window_active"))
        )
        # 等待确认按钮出来
        WebDriverWait(driver, 5000).until(
            ec.element_to_be_clickable((By.ID, "qr_submit_id"))
        )
        # 点击确认按钮，提交订单，如果要提交订单，请打开这个代码
        # confirm_btn = driver.find_element(by=By.ID, value="qr_submit_id")
        # while confirm_btn:  # 按钮有时候点不到，所以要循环点击，直到找不到这个元素，说明已经提交订单成功
        #     try:
        #         confirm_btn.click()
        #         confirm_btn = driver.find_element(by=By.ID, value="qr_submit_id")
        #     except ElementNotVisibleException:
        #         break
        #     except ElementNotInteractableException:
        #         break

        return False

    # 封装，只负责调用其他方法，用于组织其他代码
    def run(self):
        # 1.登录
        self.login()
        while True:
            # 2.余票查询
            self.search_ticket()

            # 3.确认乘车人和订单
            order_success = self.confirm()
            if order_success:
                print(now_date(), f'恭喜{self.selected_no}的{self.selected_seat}抢票成功!!!')
                break

    # 初始化车站信息
    def init_statsion_code(self):
        # 打开 JSON 文件
        with open('station.json', 'r', encoding='utf-8') as json_file:
            data = json.load(json_file)
        return data


def start():
    from_station = "北京"
    to_station = "东戴河"
    train_data = "2024-07-28"
    # O-二等座,M-一等坐,1-硬座,3-硬卧,4-软卧
    query_trains = [{"D503": ["0"]}]  # , {"G2138": ["O"]}
    passenger_name_lst = ["龙文涛"]

    spider = TrainSpider_v1(from_station, to_station, train_data, query_trains, passenger_name_lst)
    spider.run()
    # spider.init_statsion_code()


def getRun(from_station, to_station, train_data, query_trains, passenger_name_lst):
    spider = TrainSpider_v1(from_station, to_station, train_data, query_trains, passenger_name_lst)
    spider.run()


def center_window(window, width, height):
    screen_width = window.winfo_screenwidth()
    screen_height = window.winfo_screenheight()
    x_cordinate = int((screen_width / 2) - (width / 2))
    y_cordinate = int((screen_height / 2) - (height / 2))
    window.geometry("{}x{}+{}+{}".format(width, height, x_cordinate, y_cordinate))


def show_details():
    print(1111111111)
    # 起点站
    from_station = start_entry.get()
    # 终点站
    to_station = end_entry.get()
    # 日期
    train_data = date_entry.get_date()
    seat_dict = {'二等座': '0', '一等座': 'M', '硬座': '1', '硬卧': '3', '软卧': '4'}
    # 座席
    seat_list = [seat_dict[seat_var.get()]]
    train_map = {train_entry.get(): seat_list}

    # O-二等座,M-一等坐,1-硬座,3-硬卧,4-软卧
    # query_trains = [{"D503": ["0"]}]  # , {"G2138": ["O"]}
    query_trains = [train_map]
    passenger_name_lst = [name_entry.get()]

    print(f"起点: {from_station}")
    print(f"终点: {to_station}")
    print(f"日期: {train_data}")
    for query_train in query_trains:
        print(query_train)
    for n in passenger_name_lst:
        print(f"乘客：{n}")
    root.destroy()
    getRun(from_station, to_station, train_data,query_trains, passenger_name_lst)


if __name__ == '__main__':
    # 创建主窗口
    root = tk.Tk()
    root.title("火车票预订")

    # 调整窗口大小和位置
    center_window(root, 380, 150)  # 设置窗口宽度和高度

    # 创建输入框和标签
    start_label = tk.Label(root, text="起点:")
    end_label = tk.Label(root, text="终点:")
    date_label = tk.Label(root, text="日期:")
    train_label = tk.Label(root, text="车次:")
    seat_label = tk.Label(root, text="座位:")
    name_label = tk.Label(root, text="姓名:")

    start_entry = tk.Entry(root, width=20)
    end_entry = tk.Entry(root, width=20)
    date_entry = DateEntry(root, width=17,locale='zh_CN')
    train_entry = tk.Entry(root, width=20)
    seat_entry = tk.Entry(root, width=20)
    name_entry = tk.Entry(root, width=20)

    # 创建下拉菜单的变量
    seat_var = tk.StringVar(root)
    seat_var.set("硬座")  # 默认值
    seat_options = ["硬座", "无座", "二等座", "一等座", "商务座", "硬卧", "软卧"]

    # 创建下拉菜单
    # seat_menu = tk.OptionMenu(root, seat_var, *seat_options)

    # 创建下拉菜单（Combobox）
    seat_combobox = ttk.Combobox(root, textvariable=seat_var, values=seat_options, width=17)
    seat_combobox.current(0)  # 设置默认值

    # 使用grid()布局管理器进行布局

    # 使用grid()布局管理器进行布局
    start_label.grid(row=0, column=0)
    start_entry.grid(row=0, column=1)
    end_label.grid(row=0, column=2)
    end_entry.grid(row=0, column=3)

    date_label.grid(row=1, column=0)
    date_entry.grid(row=1, column=1)
    train_label.grid(row=1, column=2)
    train_entry.grid(row=1, column=3)

    name_label.grid(row=3, column=0)
    name_entry.grid(row=3, column=1)
    seat_label.grid(row=3, column=2)
    seat_combobox.grid(row=3, column=3)

    # 创建按钮
    book_button = tk.Button(root, text="预订", command=show_details)
    book_button.grid(row=5, column=0, columnspan=4)
    # 运行主循环
    root.mainloop()
